package com.zhuhjay.aspect;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zhuhjay.annotation.RecordLog;
import com.zhuhjay.enums.HttpCode;
import com.zhuhjay.exception.ApiLimiterException;
import com.zhuhjay.exception.BasicBusinessException;
import com.zhuhjay.model.entity.SystemLog;
import com.zhuhjay.service.SystemLogService;
import com.zhuhjay.utils.JacksonUtils;
import com.zhuhjay.utils.SecurityContextHolder;
import com.zhuhjay.utils.ServletUtils;
import com.zhuhjay.utils.result.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import javax.annotation.Resource;

/**
 * 记录日志切面
 * @author ZhuHJay
 * @date 2023/1/30 20:35
 */
@Order(2)
@Aspect
@Component
public class RecordLogAspect {

    @Resource
    private SystemLogService systemLogService;

    @Around("@annotation(recordLog)")
    public Object around(ProceedingJoinPoint pjp, RecordLog recordLog) throws Throwable {
        // 计时器
        StopWatch stopWatch = new StopWatch();
        Object result = null;
        Throwable t = null;
        stopWatch.start();
        try {
            // 执行真实的控制器方法
            result = pjp.proceed();
        } catch (Throwable throwable) {
            t = throwable;
        }
        stopWatch.stop();
        // 保存日志
        saveRecordLog(pjp, recordLog, result, t, stopWatch);
        // 如果异常不为空，那么就向外抛出
        if (t != null) {
            throw t;
        }
        return result;
    }

    private void saveRecordLog(ProceedingJoinPoint pjp, RecordLog recordLog,
                               Object result, Throwable t, StopWatch stopWatch) {
        SystemLog log = new SystemLog();
        Signature signature = pjp.getSignature();
        log.setMethodName(pjp.getTarget().getClass().getSimpleName() + "." + signature.getName())
                .setMethodDesc(recordLog.value())
                .setRequestMethod(ServletUtils.currentRequest().getMethod())
                .setRequestUsername(SecurityContextHolder.getUser().getUsername())
                .setRequestUrl(ServletUtils.currentRequest().getRequestURI())
                .setRequestIp(ServletUtil.getClientIP(ServletUtils.currentRequest()))
                .setRequestData(JacksonUtils.writeValueAsString(pjp.getArgs()));
        // 判断异常是否发生，来判断是否需要记录异常信息
        if (t != null) {
            // 判断异常的类型，来设置响应码
            if (t instanceof BasicBusinessException || t instanceof ApiLimiterException) {
                log.setResponseCode(HttpCode.USER_ERROR.getCode());
            } else {
                log.setResponseCode(HttpCode.SERVER_ERROR.getCode());
            }
            log.setResponseResult(JSONUtil.toJsonStr(
                    new JSONObject().set("message", t.getMessage()).set("stack", t.getStackTrace())
            ));
        } else {
            log.setResponseResult(JacksonUtils.writeValueAsString(result));
            log.setResponseCode(((Result) result).getCode());
        }
        // 记录执行时间
        log.setProcessTime((int)stopWatch.getTotalTimeMillis());
        systemLogService.save(log);
    }

}
